home *** CD-ROM | disk | FTP | other *** search
/ Hot Super Models / Hot Super Models.iso / unix / x11 / xv200.tar / xv-2.00 / xvgraf.c < prev    next >
C/C++ Source or Header  |  1992-01-02  |  22KB  |  860 lines

  1. /* 
  2.  * xvgraf.c - GRAF window handling functions
  3.  *
  4.  * callable functions:
  5.  *
  6.  *   CreateGraf()       -  creates a GRAF window
  7.  *   InitGraf()         -  inits GRAF handles to reasonable defaults
  8.  *   RedrawGraf()       -  called by 'expose' events
  9.  *   ClickGraf()        -  called when B1 clicked in GRAF window
  10.  *   GrafKey()          -  called when keypress in GRAF window
  11.  *   Graf2Str()         -  copies current GRAF settings to a string
  12.  *   Str2Graf()         -  parses an xrdb string into GRAF settings
  13.  *   GetGrafState()     -  copies GRAF data into GRAF_STATE structure
  14.  *   SetGrafState()     -  sets GRAF data based on GRAF_STATE
  15.  *   InitSpline()       -  called to generate y' table for EvalSpline
  16.  *   EvalSpline()       -  evalutes spline function at given point
  17.  */
  18.  
  19. /*
  20.  * Copyright 1989, 1990, 1991, 1992 by John Bradley and
  21.  *                       The University of Pennsylvania
  22.  *
  23.  * Permission to use, copy, and distribute for non-commercial purposes,
  24.  * is hereby granted without fee, providing that the above copyright
  25.  * notice appear in all copies and that both the copyright notice and this
  26.  * permission notice appear in supporting documentation. 
  27.  *
  28.  * The software may be modified for your own purposes, but modified versions
  29.  * may not be distributed.
  30.  *
  31.  * This software is provided "as is" without any expressed or implied warranty.
  32.  *
  33.  * The author may be contacted via:
  34.  *    US Mail:   John Bradley
  35.  *               GRASP Lab, Room 301C
  36.  *               3401 Walnut St.  
  37.  *               Philadelphia, PA  19104
  38.  *
  39.  *    Phone:     (215) 898-8813
  40.  *    EMail:     bradley@cis.upenn.edu       
  41.  */
  42.  
  43.  
  44. #include "xv.h"
  45. #include "bitmaps.h"
  46.  
  47.  
  48. #define GHIGH 147
  49. #define GBHIGH 23
  50. #define GBWIDE 30
  51. #define GWIDE (128 + 3 + 4 + GBWIDE)
  52.  
  53. #define PW gf1_addh_width
  54. #define PH gf1_addh_height
  55.  
  56.  
  57. static int    pixmaps_built = 0;
  58. static Pixmap gfbpix[N_GFB];
  59.  
  60.  
  61. /* local functions */
  62. #ifdef __STDC__
  63. static void drawGraf(GRAF *, int);
  64. #else
  65. static void drawGraf();
  66. #endif
  67.  
  68.  
  69.  
  70. /***************************************************/
  71. void CreateGraf(gp, parent, x, y, fg, bg, title)
  72. GRAF *gp;
  73. Window parent;
  74. int x,y;
  75. unsigned long fg,bg;
  76. char *title;
  77. {
  78.   /* NOTE:  CreateGraf does not initialize hands[], nhands, or spline,
  79.      as these could be initialized by X resources (or whatever),
  80.      which takes place long before we can create the windows.
  81.  
  82.      InitGraf() sets those fields of a GRAF to likely enough values */
  83.  
  84.   int i;
  85.  
  86.   if (!pixmaps_built) {
  87.     gfbpix[GFB_ADDH]   = XCreatePixmapFromBitmapData(theDisp, parent, 
  88.                     gf1_addh_bits, PW, PH, 1, 0, 1);
  89.     gfbpix[GFB_DELH]   = XCreatePixmapFromBitmapData(theDisp, parent, 
  90.                     gf1_delh_bits, PW, PH, 1, 0, 1);
  91.     gfbpix[GFB_LINE]   = XCreatePixmapFromBitmapData(theDisp, parent, 
  92.                             gf1_line_bits, PW, PH, 1, 0, 1);
  93.     gfbpix[GFB_SPLINE] = XCreatePixmapFromBitmapData(theDisp, parent, 
  94.                             gf1_spln_bits, PW, PH, 1, 0, 1);
  95.     gfbpix[GFB_RESET]  = XCreatePixmapFromBitmapData(theDisp, parent, 
  96.                     gf1_rst_bits, PW, PH, 1, 0, 1);
  97.     gfbpix[GFB_GAMMA]  = XCreatePixmapFromBitmapData(theDisp, parent, 
  98.         gf1_gamma_bits, gf1_gamma_width, gf1_gamma_height, 1, 0, 1);
  99.  
  100.     for (i=0; i<N_GFB && gfbpix[i] != (Pixmap) NULL; i++);
  101.     if (i<N_GFB) FatalError("can't create graph pixmaps");
  102.  
  103.     pixmaps_built = 1;
  104.   }
  105.  
  106.   gp->fg     = fg;
  107.   gp->bg     = bg;
  108.   gp->str    = title;
  109.   gp->entergamma = 0;
  110.   sprintf(gp->gvstr, "%.5g", gp->gamma);
  111.  
  112.   gp->win = XCreateSimpleWindow(theDisp, parent, x,y, GWIDE, GHIGH, 1, fg,bg);
  113.   if (!gp->win) FatalError("can't create graph (main) window");
  114.  
  115.   gp->gwin = XCreateSimpleWindow(theDisp, gp->win, 2, GHIGH-132, 
  116.                  128, 128, 1, fg,bg);
  117.   if (!gp->gwin) FatalError("can't create graph (sub) window");
  118.  
  119.   for (i=0; i<N_GFB; i++) {
  120.     BTCreate(&gp->butts[i], gp->win, GWIDE-GBWIDE-2, 1+i * (GBHIGH + 1),
  121.          GBWIDE, GBHIGH, (char *) NULL, fg, bg);
  122.     gp->butts[i].pix = gfbpix[i];
  123.     gp->butts[i].pw = PW;  
  124.     gp->butts[i].ph = PH;
  125.   }
  126.  
  127.   gp->butts[GFB_SPLINE].toggle = 1;
  128.   gp->butts[GFB_LINE  ].toggle = 1;
  129.  
  130.   gp->butts[GFB_SPLINE].lit =  gp->spline;
  131.   gp->butts[GFB_LINE  ].lit = !gp->spline;
  132.  
  133.   if (gp->nhands == 2)          gp->butts[GFB_DELH].active = 0;
  134.   if (gp->nhands == MAX_GHANDS) gp->butts[GFB_ADDH].active = 0;
  135.  
  136.   GenerateGrafFunc(gp,0);
  137.   XSelectInput(theDisp, gp->win, ExposureMask | ButtonPressMask |
  138.            KeyPressMask);
  139.  
  140.   XMapSubwindows(theDisp, gp->win);
  141. }
  142.  
  143.  
  144. /***************************************************/
  145. void InitGraf(gp)
  146. GRAF *gp;
  147. {
  148.   gp->nhands = 4;
  149.   gp->spline = 1;
  150.   gp->hands[0].x =   0;  gp->hands[0].y =   0;
  151.   gp->hands[1].x =  32;  gp->hands[1].y =  32;
  152.   gp->hands[2].x =  96;  gp->hands[2].y =  96;
  153.   gp->hands[3].x = 127;  gp->hands[3].y = 127;
  154.  
  155.   gp->gammamode = 0;     gp->gamma = 1.0;
  156. }
  157.   
  158.  
  159. /***************************************************/
  160. void RedrawGraf(gp,x,y,w,h)
  161. GRAF *gp;
  162. int x,y,w,h;
  163. {
  164.   int i;
  165.   XRectangle xr;
  166.  
  167. #ifdef FOO
  168.   xr.x = x;  xr.y = y;  xr.width = w;  xr.height = h;
  169.   XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted);
  170. #endif
  171.  
  172.   XSetForeground(theDisp, theGC, gp->fg);
  173.   XSetBackground(theDisp, theGC, gp->bg);
  174.  
  175.   /* redraw title */
  176.   XDrawString(theDisp, gp->win, theGC, 2, 1+ASCENT, gp->str, strlen(gp->str));
  177.  
  178.   /* redraw buttons */
  179.   for (i=0; i<N_GFB; i++) BTRedraw(&gp->butts[i]);
  180.  
  181. #ifdef FOO
  182.   XSetClipMask(theDisp, theGC, None);
  183. #endif
  184.  
  185.   /* redraw graph */
  186.   drawGraf(gp,0);
  187. }
  188.  
  189.  
  190. /***************************************************/
  191. static void drawGraf(gp,erase)
  192. GRAF *gp;
  193. int   erase;
  194. {
  195.   int i,x,y;
  196.   XPoint  pts[129], *pt;
  197.   
  198.  
  199.   if (gp->entergamma) {
  200.     char *str1 = "Enter gamma";
  201.     char *str2 = "value: ";
  202.  
  203.     XSetForeground(theDisp, theGC, gp->fg);
  204.     XSetBackground(theDisp, theGC, gp->bg);
  205.  
  206.     XClearWindow(theDisp,gp->gwin);
  207.     XDrawString(theDisp,gp->gwin, theGC, 10,30+ASCENT, str1, strlen(str1));
  208.     XDrawString(theDisp,gp->gwin, theGC, 10,30+ASCENT+CHIGH+3, 
  209.         str2, strlen(str2));
  210.  
  211.     x = 10 + StringWidth(str2) + 8;
  212.     y = 30 + ASCENT + CHIGH + 3;
  213.     i = StringWidth(gp->gvstr);
  214.     if (gp->entergamma < 0 && strlen(gp->gvstr)) { 
  215.       /* show string highlited */
  216.       XFillRectangle(theDisp, gp->gwin, theGC, x-1,y-ASCENT-1,i+2,CHIGH+2);
  217.       XSetForeground(theDisp, theGC, gp->bg);
  218.     }
  219.     else 
  220.       XDrawLine(theDisp, gp->gwin, theGC, x+i, y-ASCENT, x+i, y+DESCENT);
  221.       
  222.     XDrawString(theDisp, gp->gwin, theGC, x,y, gp->gvstr, strlen(gp->gvstr));
  223.  
  224.     return;
  225.   }
  226.  
  227.   if (erase) XSetForeground(theDisp, theGC, gp->bg);
  228.         else XSetForeground(theDisp, theGC, gp->fg);
  229.  
  230.   for (i=0, pt=pts; i<256; i+=2,pt++) {
  231.     pt->x = i/2;  pt->y = 127 - (gp->func[i]/2);
  232.     if (i==0) i = -1;   /* kludge to get sequence 0,1,3,5, ... 253,255 */
  233.   }
  234.   XDrawLines(theDisp, gp->gwin, theGC, pts, 129, CoordModeOrigin);
  235.  
  236.   if (erase) return;   /* don't erase handles */
  237.  
  238.  
  239.   /* redraw handles */
  240.  
  241.   XSetForeground(theDisp, theGC, gp->bg);
  242.  
  243.   for (i=0; i<gp->nhands; i++) {   /* clear inside rectangles */
  244.     x = gp->hands[i].x;  y = 127 - gp->hands[i].y;
  245.     XFillRectangle(theDisp, gp->gwin, theGC, x-2, y-2, 5,5);
  246.   }
  247.  
  248.   XSetForeground(theDisp,theGC,gp->fg);
  249.  
  250.   for (i=0; i<gp->nhands; i++) {  /* draw center dots */
  251.     x = gp->hands[i].x;  y = 127 - gp->hands[i].y;
  252.     XDrawPoint(theDisp, gp->gwin, theGC, x, y);
  253.   }
  254.  
  255.   for (i=0; i<gp->nhands; i++) {   /* draw rectangles */
  256.     x = gp->hands[i].x;  y = 127 - gp->hands[i].y;
  257.     XDrawRectangle(theDisp, gp->gwin, theGC, x-3, y-3, 6,6);
  258.   }
  259.  
  260. }
  261.  
  262.  
  263. /***************************************************/
  264. int ClickGraf(gp,child,mx,my)
  265. GRAF *gp;
  266. Window child;
  267. int mx,my;
  268. {
  269.   /* returns '1' if GrafFunc was changed, '0' otherwise */
  270.  
  271.   int          i, j, rv;
  272.   byte         oldfunc[256];
  273.   BUTT        *bp;
  274.   Window       rW, cW;
  275.   int          x, y, rx, ry, firsttime=1;
  276.   unsigned int mask;
  277.  
  278.   rv = 0;
  279.  
  280.   while (1) {   /* loop until Button1 up and ShiftKey up */
  281.     if (!XQueryPointer(theDisp,gp->win,&rW,&cW,&rx,&ry,
  282.                &mx,&my,&mask)) continue;
  283.     if (!firsttime && !(mask & (Button1Mask | ShiftMask))) break;
  284.  
  285.     /* if it's not the first time, wait for Button1 to be pressed */
  286.     if (!firsttime && !(mask & Button1Mask)) continue;
  287.  
  288.     firsttime = 0;
  289.  
  290.     for (i=0; i<N_GFB; i++) {
  291.       bp = &gp->butts[i];
  292.       if (PTINRECT(mx, my, bp->x, bp->y, bp->w, bp->h)) break;
  293.     }
  294.  
  295.     if (i<N_GFB) {  /* found one */
  296.       if (BTTrack(bp)) {  /* it was selected */
  297.     switch (i) {
  298.     case GFB_SPLINE: 
  299.     case GFB_LINE:
  300.       gp->gammamode = 0;
  301.  
  302.       if ((i==GFB_SPLINE && !gp->spline) ||
  303.           (i==GFB_LINE   &&  gp->spline)) {
  304.         gp->spline = !gp->spline;
  305.         gp->butts[GFB_SPLINE].lit =  gp->spline;
  306.         gp->butts[GFB_LINE].lit   = !gp->spline;
  307.         BTRedraw(&gp->butts[GFB_SPLINE]);
  308.         BTRedraw(&gp->butts[GFB_LINE]);
  309.  
  310.         for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
  311.         GenerateGrafFunc(gp,1);
  312.         for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
  313.         if (i<256) rv = 1;
  314.       }
  315.       else {
  316.         gp->butts[i].lit = 1;
  317.         BTRedraw(&gp->butts[i]);
  318.       }
  319.       break;
  320.  
  321.     case GFB_RESET:
  322.       for (j=0; j<gp->nhands; j++) {
  323.         gp->hands[j].y = gp->hands[j].x;
  324.       }
  325.       gp->gammamode = 0;
  326.  
  327.       for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
  328.       GenerateGrafFunc(gp,1);
  329.       for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
  330.       if (i<256) rv = 1;
  331.  
  332.       break;
  333.  
  334.     case GFB_GAMMA:
  335.       gp->entergamma = -1;
  336.       drawGraf(gp,1);
  337.       break;
  338.  
  339.     case GFB_ADDH:
  340.       if (gp->nhands < MAX_GHANDS) {
  341.         /* find largest x-gap in handles, put new handle in mid */
  342.         int lgap, lpos, x, y;
  343.         
  344.         lgap = gp->hands[1].x - gp->hands[0].x;
  345.         lpos = 1;
  346.         for (j=1; j<gp->nhands-1; j++)
  347.           if ((gp->hands[j+1].x - gp->hands[j].x) > lgap) {
  348.         lgap = gp->hands[j+1].x - gp->hands[j].x;
  349.         lpos = j+1;
  350.           }
  351.       
  352.         /* open up position in hands[] array */
  353.         bcopy(&gp->hands[lpos], &gp->hands[lpos+1], 
  354.           (gp->nhands - lpos) * sizeof(XPoint));
  355.       
  356.         x = gp->hands[lpos-1].x * 2 + lgap;
  357.         y = gp->func[x];
  358.         gp->hands[lpos].x = x/2;
  359.         gp->hands[lpos].y = y/2;
  360.         gp->nhands++;
  361.  
  362.         for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
  363.         GenerateGrafFunc(gp,1);
  364.         for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
  365.         if (i<256) rv = 1;
  366.  
  367.         if (gp->nhands==MAX_GHANDS)   /* turn off 'add' button */
  368.           BTSetActive(&gp->butts[GFB_ADDH], 0);
  369.  
  370.         if (gp->nhands==3)            /* turn on 'del' button */
  371.           BTSetActive(&gp->butts[GFB_DELH], 1);
  372.       }
  373.       break;
  374.                
  375.     case GFB_DELH:
  376.       if (gp->nhands > 2) {
  377.         /* find (middle) point whose x-distance to previous
  378.            and next points is minimal.  Delete that point */
  379.         int dist, mdist, mpos;
  380.  
  381.         mdist = (gp->hands[1].x - gp->hands[0].x) +
  382.                 (gp->hands[2].x - gp->hands[1].x);
  383.         mpos = 1;
  384.  
  385.         for (j=2; j<gp->nhands-1; j++) {
  386.           dist = (gp->hands[j  ].x - gp->hands[j-1].x) +
  387.         (gp->hands[j+1].x - gp->hands[j].x);
  388.           if (dist < mdist) {
  389.         mdist = dist;  mpos = j;
  390.           }
  391.         }
  392.                
  393.         /* delete position 'mpos' in hands[] array */
  394.         bcopy(&gp->hands[mpos+1], &gp->hands[mpos], 
  395.           (gp->nhands-mpos-1) * sizeof(XPoint));
  396.  
  397.         gp->nhands--;
  398.         for (i=0; i<256; i++) oldfunc[i] = gp->func[i];
  399.         GenerateGrafFunc(gp,1);
  400.         for (i=0; i<256 && abs(oldfunc[i] - gp->func[i])<2; i++);
  401.         if (i<256) rv = 1;
  402.  
  403.         if (gp->nhands==MAX_GHANDS-1) /* turn on 'add' button */
  404.           BTSetActive(&gp->butts[GFB_ADDH], 1);
  405.  
  406.         if (gp->nhands==2)            /* turn off 'del' button */
  407.           BTSetActive(&gp->butts[GFB_DELH], 0);
  408.       }
  409.       break;
  410.     }
  411.       }
  412.     }
  413.  
  414.  
  415.     else if (cW == gp->gwin) {  /* clicked in graph */
  416.       int h, vertonly, offx, offy;
  417.  
  418.       XTranslateCoordinates(theDisp, gp->win, gp->gwin,mx,my,&mx,&my,&cW);
  419.       my = 127 - my;   /* flip y axis */
  420.  
  421.       /* see if x,y is within any of the handles */
  422.       for (h=0; h<gp->nhands; h++) {
  423.     if (PTINRECT(mx,my,gp->hands[h].x-5,gp->hands[h].y-5,11,11)) break;
  424.       }
  425.  
  426.       if (h==gp->nhands) {     /* not found.  wait 'til mouseup */
  427.     while (XQueryPointer(theDisp,gp->gwin,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  428.       if (!(mask & Button1Mask)) break;    /* button released */
  429.     }
  430.       }
  431.       else {  /* track handle */
  432.     gp->gammamode = 0;
  433.     offx = gp->hands[h].x - mx;  
  434.     offy = gp->hands[h].y - my;
  435.  
  436.     vertonly = (h==0 || h==(gp->nhands-1));
  437.  
  438.     while (XQueryPointer(theDisp,gp->gwin,&rW,&cW,&rx,&ry,&x,&y,&mask)) {
  439.       if (!(mask & Button1Mask)) break;    /* button released */
  440.       y = 127 - y;   /* flip y-axis */
  441.  
  442.       if (vertonly) x = mx;  /* no sidewards motion */
  443.       else { /* keep this handle between its neighbors */
  444.         if (x+offx <= gp->hands[h-1].x) x = (gp->hands[h-1].x+1)-offx;
  445.         if (x+offx >= gp->hands[h+1].x) x = (gp->hands[h+1].x-1)-offx;
  446.       }
  447.  
  448.       if (mx != x || my != y) {   /* this handle has moved... */
  449.         XSetForeground(theDisp, theGC, gp->bg);
  450.         XFillRectangle(theDisp, gp->gwin, theGC, 
  451.                gp->hands[h].x-3, (127-gp->hands[h].y)-3, 7,7);
  452.  
  453.         gp->hands[h].x = x+offx;  gp->hands[h].y = y+offy;
  454.         RANGE(gp->hands[h].y,0,127);
  455.         drawGraf(gp,1);           /* erase old trace */
  456.         GenerateGrafFunc(gp,0);
  457.         drawGraf(gp,0);
  458.         rv = 1;
  459.       }
  460.       mx = x;  my = y;
  461.     }
  462.       }
  463.     }
  464.   }
  465.  
  466.   return rv;
  467. }
  468.  
  469.  
  470. /***************************************************/
  471. int GrafKey(gp,str)
  472. GRAF *gp;
  473. char *str;
  474. {
  475.   int len, ok;
  476.  
  477.   /* returns '1' if str was 'eaten', '2' if CR was hit, '0' otherwise. */
  478.  
  479.   if (!gp->entergamma) {   /* not entering a value yet */
  480.     if (*str == 'g') {
  481.       gp->entergamma = -1;   /* special 'show old value highlited' */
  482.       drawGraf(gp,1);
  483.       str++;
  484.     }
  485.     else return 0;
  486.   }
  487.  
  488.   while (*str) {
  489.     if (gp->entergamma == -1 && 
  490.     (*str != '\012' && *str != '\015' && *str != '\033')) {
  491.       gp->entergamma = 1;
  492.       gp->gvstr[0] = '\0';
  493.       drawGraf(gp,1);
  494.     }
  495.  
  496.     ok = 0;
  497.     len = strlen(gp->gvstr);
  498.  
  499.     if (*str>= '0' && *str <= '9') {
  500.       if (len < GVMAX) { 
  501.     gp->gvstr[len++] = *str;
  502.       gp->gvstr[len] = '\0';
  503.     ok = 1;
  504.       }
  505.     }
  506.  
  507.     else if (*str == '.') {
  508.       if (len < GVMAX && strchr(gp->gvstr,'.')==NULL) {
  509.     gp->gvstr[len++] = *str;
  510.     gp->gvstr[len] = '\0';
  511.     ok = 1;
  512.       }
  513.     }
  514.  
  515.     else if (*str == '\010' || *str == '\177') {   /* BS or DEL */
  516.       if (len > 0) gp->gvstr[--len] = '\0';
  517.       ok = 1;
  518.     }
  519.  
  520.     else if (*str == '\025' || *str == '\013') {   /* ^U or ^K clear line */
  521.       gp->gvstr[0] = '\0';  len = 0;  ok = 1;
  522.     }
  523.  
  524.     else if (*str == '\012' || *str == '\015' || *str == '\033') {
  525.       /* CR, LF or ESC*/
  526.       if (len>0 && *str != '\033') {   /* 'Ok' processing */
  527.     if (sscanf(gp->gvstr, "%lf", &(gp->gamma))==1) {
  528.       if (gp->gamma == 0.0) gp->gamma = 0.001;
  529.       if (gp->gamma >= 1000.0) gp->gamma = 1000.0;
  530.       gp->gammamode = 1;
  531.       gp->entergamma = 0;
  532.       GenerateGrafFunc(gp,1);
  533.       return 2;
  534.     }
  535.       }
  536.  
  537.       else {
  538.     gp->entergamma = 0;
  539.     GenerateGrafFunc(gp,1);
  540.       }
  541.       break;   /* out of *str loop */
  542.     }
  543.  
  544.     if (!ok) XBell(theDisp, 0);
  545.     else {
  546.       XClearWindow(theDisp,gp->gwin);
  547.       drawGraf(gp,1);
  548.     }
  549.  
  550.     str++;
  551.   }
  552.  
  553.   return 1;
  554. }
  555.  
  556.  
  557.  
  558. /*********************/
  559. void GenerateGrafFunc(gp,redraw)
  560. GRAF *gp;
  561. int redraw;
  562. {
  563.   /* generate new gp->func data (ie, handles have moved, or line/spline
  564.      setting has changed) and redraw the entire graph area */
  565.  
  566.   int i,j,k;
  567.   static int x[MAX_GHANDS], y[MAX_GHANDS];
  568.   double yf[MAX_GHANDS];
  569.  
  570.   /* do sanity check.  (x-coords must be sorted (strictly increasing)) */
  571.  
  572.   for (i=0; i<gp->nhands; i++) { 
  573.     RANGE(gp->hands[i].x, 0, 127); 
  574.     RANGE(gp->hands[i].y, 0, 127);
  575.   }
  576.  
  577.   gp->hands[0].x = 0;  gp->hands[gp->nhands-1].x = 127;
  578.   for (i=1; i<gp->nhands-1; i++) {
  579.     if (gp->hands[i].x < i)  gp->hands[i].x = i;
  580.     if (gp->hands[i].x > 128-gp->nhands+i)  
  581.         gp->hands[i].x = 128-gp->nhands+i;
  582.  
  583.     if (gp->hands[i].x <= gp->hands[i-1].x) 
  584.       gp->hands[i].x = gp->hands[i-1].x + 1;
  585.   }
  586.  
  587.   /* recompute the function */
  588.  
  589.   if (gp->gammamode) {  /* do gamma function instead of interpolation */
  590.     double y, invgam;
  591.     invgam = 1.0 / gp->gamma;
  592.     for (i=0; i<256; i++) {
  593.       y = pow( ((double) i / 255.0), invgam) * 255.0;
  594.       j = (int) y;
  595.       RANGE(j,0,255);
  596.       gp->func[i] = j;
  597.     }
  598.  
  599.     for (i=0; i<gp->nhands; i++) {
  600.       gp->hands[i].y = gp->func[ gp->hands[i].x * 2] / 2;
  601.     }
  602.   }
  603.    
  604.   else if (!gp->spline) {  /* do linear interpolation */
  605.       int y,x1,y1,x2,y2,kldg;
  606.       
  607.       for (i=0; i<gp->nhands-1; i++) {
  608.     x1 = gp->hands[ i ].x * 2;  y1 = gp->hands[ i ].y * 2;
  609.     x2 = gp->hands[i+1].x * 2;  y2 = gp->hands[i+1].y * 2;
  610.  
  611.     for (j=x1,k=0; j<=x2+1; j++,k++) {  /* x2 <= 254 */
  612.       y = y1 + (int) (((double) k * (y2 - y1)) / (x2 - x1));
  613.       RANGE(y,0,255);
  614.       gp->func[j] = y;
  615.     }
  616.       }
  617.     }
  618.  
  619.   else {  /* splinear interpolation */
  620.     for (i=0; i<gp->nhands; i++) { 
  621.       x[i] = gp->hands[i].x*2;  y[i] = gp->hands[i].y*2;
  622.     }
  623.     
  624.     InitSpline(x, y, gp->nhands, yf);
  625.   
  626.     for (i=0; i<256; i++) {
  627.       j = (int) EvalSpline(x, y, yf, gp->nhands, (double) i);
  628.       RANGE(j,0,255);
  629.       gp->func[i] = j;
  630.     }
  631.   }
  632.  
  633.  
  634.   if (redraw) {  /* redraw graph */
  635.     XClearWindow(theDisp, gp->gwin);
  636.     drawGraf(gp,0);
  637.   }
  638. }
  639.  
  640.  
  641. /*********************/
  642. void Graf2Str(gp, str)
  643. GRAF_STATE *gp;
  644. char *str;
  645. {
  646.   /* generates strings of the form: "S 3 : 0,0 : 63,63 : 255,255",
  647.      (meaning SPLINE, 3 points, and the 3 sets of handle coordinates)
  648.      This is the string that you'd put in the 'xv.preset1.vgraph:' resource,
  649.      ferinstance */
  650.  
  651.   /* maximum length of string generated is 164 characters.  str better be
  652.      able to hold it... */
  653.  
  654.   int i;
  655.   char cstr[16];
  656.  
  657.   if (gp->gammamode) {
  658.     sprintf(str,"G %g", gp->gamma);
  659.   }
  660.   else {
  661.     sprintf(str, "%c %d", gp->spline ? 'S' : 'L', gp->nhands);
  662.     for (i=0; i<gp->nhands; i++) {
  663.       sprintf(cstr," : %d,%d", gp->hands[i].x*2, gp->hands[i].y*2);
  664.       strcat(str, cstr);
  665.     }
  666.   }
  667. }
  668.  
  669.  
  670. /*********************/
  671. int Str2Graf(gp, str)
  672. GRAF_STATE *gp;
  673. char *str;
  674. {
  675.   /* parses strings of the form: "S 3 : 0,0 : 63,63 : 255,255",
  676.      (meaning SPLINE, 3 points, and the 3 sets of handle coordinates)
  677.      This is the string that you'd put in the 'xv.preset1.igraf:' resource,
  678.      ferinstance */
  679.  
  680.   /* returns '1' if unable to parse.  Note:  does NOT redraw the graf, as
  681.      it may be called before the graph window has even been created */
  682.  
  683.   /* NOTE: I deliberately avoid using '*dp++ = *sp++', as this sort of
  684.      thing tends to break optimizers */
  685.  
  686.   char   tstr[256], tstr1[256], *sp, *dp;
  687.   XPoint coords[MAX_GHANDS];
  688.   int    spline, nhands, i, x, y;
  689.  
  690.   if (!str) return 1;  /* NULL strings don't parse well! */
  691.  
  692.   /* first, strip all pesky whitespace from str */
  693.   for (sp=str, dp=tstr; *sp; sp++) 
  694.     if (*sp > ' ') { *dp = *sp;  dp++; }
  695.   *dp = '\0';
  696.  
  697.   /* check for 'gamma'-style str */
  698.   if (*tstr == 'G' || *tstr == 'g') {
  699.     if (sscanf(tstr+1, "%lf", &(gp->gamma)) == 1 &&
  700.     gp->gamma > 0.0 && gp->gamma <= 1000.0) {
  701.       gp->gammamode = 1;
  702.       sprintf(gp->gvstr, "%.5g", gp->gamma);
  703.       return 0;
  704.       }
  705.     else return 1;
  706.   }
  707.     
  708.   /* read Spline, or Line (S/L) character */
  709.   sp = tstr;
  710.   if      (*sp == 'S' || *sp == 's') spline = 1;
  711.   else if (*sp == 'L' || *sp == 'l') spline = 0;
  712.   else return 1;
  713.  
  714.   /* read 'nhands' */
  715.   sp++;  dp = tstr1;
  716.   while (*sp && *sp != ':') { *dp = *sp;  dp++;  sp++; }
  717.   *dp++ = '\0';
  718.   nhands = atoi(tstr1);
  719.   if (nhands>MAX_GHANDS || nhands<2) return 1;
  720.  
  721.   /* read nhands coordinate pairs */
  722.   for (i=0; i<nhands && *sp; i++) {
  723.     sp++;  dp = tstr1;
  724.     while (*sp && *sp != ':') {*dp = *sp;  dp++;  sp++; }
  725.     *dp++ = '\0';
  726.     if (sscanf(tstr1,"%d,%d",&x, &y) != 2) return 1;
  727.     if (x < 0 || x > 255 || 
  728.     y < 0 || y > 255) return 1;  /* out of range */
  729.     coords[i].x = x;  coords[i].y = y;
  730.   }
  731.  
  732.   if (i<nhands) return 1;  /* string terminated early */
  733.  
  734.   gp->nhands = nhands;  gp->spline = spline;
  735.   for (i=0; i<nhands; i++) {
  736.     gp->hands[i].x = coords[i].x / 2;
  737.     gp->hands[i].y = coords[i].y / 2;
  738.   }
  739.  
  740.   return 0;
  741. }
  742.  
  743.  
  744.  
  745. /*********************/
  746. void GetGrafState(gp, gsp)
  747. GRAF *gp;
  748. GRAF_STATE *gsp;
  749. {
  750.   int i;
  751.  
  752.   gsp->spline = gp->spline;
  753.   gsp->entergamma= gp->entergamma;
  754.   gsp->gammamode = gp->gammamode;
  755.   gsp->gamma = gp->gamma;
  756.   gsp->nhands = gp->nhands;
  757.   strcpy(gsp->gvstr, gp->gvstr);
  758.   for (i=0; i<MAX_GHANDS; i++) {
  759.     gsp->hands[i].x = gp->hands[i].x;
  760.     gsp->hands[i].y = gp->hands[i].y;
  761.   }
  762. }
  763.  
  764.  
  765. /*********************/
  766. int SetGrafState(gp, gsp)
  767. GRAF *gp;
  768. GRAF_STATE *gsp;
  769. {
  770. #define IFSET(a,b) if ((a) != (b)) { a = b;  rv++; }
  771.   int i;
  772.   int rv = 0;
  773.  
  774.   IFSET(gp->spline,     gsp->spline);
  775.   IFSET(gp->entergamma, gsp->entergamma);
  776.   IFSET(gp->gammamode,  gsp->gammamode);
  777.   IFSET(gp->gamma,      gsp->gamma);
  778.   IFSET(gp->nhands,     gsp->nhands);
  779.  
  780.   if (strcmp(gp->gvstr, gsp->gvstr)) 
  781.     { strcpy(gp->gvstr, gsp->gvstr);  rv++; }
  782.  
  783.   for (i=0; i<gp->nhands; i++) {
  784.     IFSET(gp->hands[i].x, gsp->hands[i].x);
  785.     IFSET(gp->hands[i].y, gsp->hands[i].y);
  786.   }
  787.  
  788.   gp->butts[GFB_DELH].active = (gp->nhands > 2);
  789.   gp->butts[GFB_ADDH].active = (gp->nhands < MAX_GHANDS);
  790.  
  791.   gp->butts[GFB_SPLINE].lit =  gp->spline;
  792.   gp->butts[GFB_LINE].lit   = !gp->spline;
  793.  
  794.   if (rv) {
  795.     XClearWindow(theDisp,gp->gwin);
  796.     GenerateGrafFunc(gp,0);
  797.     RedrawGraf(gp,0,0,200,200);
  798.   }
  799.  
  800.   return rv;
  801. }
  802.  
  803.  
  804. /*********************/
  805. void InitSpline(x,y,n,y2)
  806.      int *x, *y, n;
  807.      double *y2;
  808. {
  809.   /* given arrays of data points x[0..n-1] and y[0..n-1], computes the
  810.      values of the second derivative at each of the data points
  811.      y2[0..n-1] for use in the splint function */
  812.  
  813.   int i,k;
  814.   double p,qn,sig,un,u[MAX_GHANDS];
  815.  
  816.   y2[0] = u[0] = 0.0;
  817.  
  818.   for (i=1; i<n-1; i++) {
  819.     sig = ((double) x[i]-x[i-1]) / ((double) x[i+1] - x[i-1]);
  820.     p = sig * y2[i-1] + 2.0;
  821.     y2[i] = (sig-1.0) / p;
  822.     u[i] = (((double) y[i+1]-y[i]) / (x[i+1]-x[i])) - 
  823.            (((double) y[i]-y[i-1]) / (x[i]-x[i-1]));
  824.     u[i] = (6.0 * u[i]/(x[i+1]-x[i-1]) - sig*u[i-1]) / p;
  825.   }
  826.   qn = un = 0.0;
  827.  
  828.   y2[n-1] = (un-qn*u[n-2]) / (qn*y2[n-2]+1.0);
  829.   for (k=n-2; k>=0; k--)
  830.     y2[k] = y2[k]*y2[k+1]+u[k];
  831. }
  832.  
  833.  
  834.  
  835. /*********************/
  836. double EvalSpline(xa,ya,y2a,n,x)
  837. double y2a[],x;
  838. int n,xa[],ya[];
  839. {
  840.   int klo,khi,k;
  841.   double h,b,a;
  842.  
  843.   klo = 0;
  844.   khi = n-1;
  845.   while (khi-klo > 1) {
  846.     k = (khi+klo) >> 1;
  847.     if (xa[k] > x) khi = k;
  848.     else klo = k;
  849.   }
  850.   h = xa[khi] - xa[klo];
  851.   if (h==0.0) FatalError("bad xvalues in splint\n");
  852.   a = (xa[khi]-x)/h;
  853.   b = (x-xa[klo])/h;
  854.   return (a*ya[klo] + b*ya[khi] + ((a*a*a-a)*y2a[klo] +(b*b*b-b)*y2a[khi]) 
  855.       * (h*h) / 6.0);
  856. }
  857.     
  858.  
  859.  
  860.